/**
 * @class BGM等の音源を表すクラスです。
 *      主ボリュームのみを持つ単純なユニットであるGainOnlyUnitを継承したクラスで、
 *      <a href="http://g200kg.github.io/web-audio-api-ja/#AudioBufferSourceNode">WebAudioAPIのAudioBufferSourceNode</a>をラップします。
 * @param {String} id オブジェクトを識別するID
 * @extends {snd.GainOnlyUnit} 
 */
snd.Sound = function(id) {
    snd.GainOnlyUnit.apply(this, arguments);

    // properties
    this.id = id;
    this.status = snd.status.NONE;
    this.type = snd.srctype.NONE;

    this.source = null;
    this.audioBuffer = null;
};
snd.Sound.prototype = Object.create(snd.GainOnlyUnit.prototype);
snd.Sound.prototype.constructor = snd.Sound;

/**
 * この音源の再生を開始します。
 *      引数の内容については、<a href="http://g200kg.github.io/web-audio-api-ja/#AudioBufferSourceNode">Web Audio API仕様のAudioBufferSourceNode</a>を参照してください。
 * @param {Number} when 再生を開始する時刻(秒)
 * @param {Number} offset 再生を開始する位置(秒)
 * @param {Number} duration 再生する長さ(秒)
 */
snd.Sound.prototype.start = function(when, offset, duration) {
    if (this.source != null && this.status == snd.status.READY) {
        if (offset == null) {
            this.source.start(when);
        } else if (duration == null) {
            this.source.start(when, offset);
        } else {
            this.source.start(when, offset, duration);
        }
        this.status = snd.status.STARTED;
    } else {
        if (this.audioBuffer != null && this.type == snd.srctype.AUDIO_BUFFER) {
            this.setAudioBuffer(this.audioBuffer);
            this.start(when, offset, duration);
        }
    }
};

/**
 * 再生を停止します。
 * @param {double} when
 */
snd.Sound.prototype.stop = function(when) {
    if (this.source != null) {
        if (when == null) {
            this.source.stop();
        } else {
            this.source.stop(when);
        }
    }
};

/**
 * この音源がループするかどうかを設定します。
 *      引数の内容については、<a href="http://g200kg.github.io/web-audio-api-ja/#AudioBufferSourceNode">Web Audio API仕様のAudioBufferSourceNode</a>を参照してください。
 * @param {boolean} status ループするか否か
 */
snd.Sound.prototype.setLoop = function(status) {
    if (this.source != null) {
        this.source.loop = status;
    }
};

/**
 * ループの開始位置を設定します。
 * @param {double} when
 */
snd.Sound.prototype.setLoopStart = function(when) {
    if (this.source != null && when != null) {
        this.source.loopStart = when;
    }
};

/**
 * ループの終端を設定します。
 * @param {double} when
 */
snd.Sound.prototype.setLoopEnd = function(when) {
    if (this.source != null && when != null) {
        this.source.loopEnd = when;
    }
};

/**
 * 再生終了時に呼び出されるメソッドです。<br>
 * 内部でstatusアトリビュートにsnd.status.ENDEDを設定しているため、このメソッドを
 * オーバーライドする際は、オーバーライドしたメソッド内でsnd.Sound.prototype.onended.call(this)を使用して
 * 親クラスのonendedを呼び出すようにしてください。
 */
snd.Sound.prototype.onended = function() {
    this.status = snd.status.ENDED;
};

/**
 * この音源のソースとして、AudioBufferSourceNodeを設定します。<br>
 * 読み込んだデータはこのオブジェクトに保持され、次回以降の再生に自動的に再利用されます。<br>
 * また、読込終了時にtypeアトリビュートにsnd.type.AUDIO_BUFFERが設定され、同時にonloadメソッドが呼ばれます。
 * 
 * @param {String} url この音源のソースとなるデータのURL
 */
snd.Sound.prototype.load = function(url) {
    var _this = this;
    var request = new XMLHttpRequest();

    request.open('GET', url, true);
    request.responseType = 'arraybuffer';

    request.onload = function() {
        snd.AUDIO_CONTEXT.decodeAudioData(
                request.response,
                function(buf) {
                    _this.setAudioBuffer(buf);
                });
    };

    request.send();
};

/**
 * オーディオバッファを設定するメソッドです。<br>
 * 自動的にAudioContextからAudioBufferSourceNodeを生成し、このオブジェクトのソースとして使用するように設定します。<br>
 * 同時に、typeアトリビュートの値がsnd.srctype.AUDIO_BUFFERに設定されます。
 * 
 * @param {type} audioBuffer
 */
snd.Sound.prototype.setAudioBuffer = function(audioBuffer) {
    this.audioBuffer = audioBuffer;

    var src = snd.AUDIO_CONTEXT.createBufferSource();
    this.source = src;

    this.source.buffer = this.audioBuffer;
    this.connectSource(this.source);
    this.source.onended = this.onended;

    this.type = snd.srctype.AUDIO_BUFFER;
    this.status = snd.status.READY;

    this.onload();
};
